package com.leenmvc.core.security;

import com.leenmvc.core.init.Constant;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.AbstractShiroFilter;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Shiro 配置
 *
 * @author Levin
 */
@Configuration
public class ShiroConfiguration {

    private static final Logger log = LoggerFactory.getLogger(ShiroConfiguration.class);

    /**
     * ehcache缓存
     *
     * @return
     */
//    @Bean
//    public EhCacheManager getEhCacheManager() {
//        EhCacheManager em = new EhCacheManager();
//        em.setCacheManagerConfigFile("classpath:ehcache-shiro.xml");
//        return em;
//    }

    /**
     *
     * @return
     */
    @Bean(name = "lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * 注入自定义的密码校验器
     *
     * @return
     */
//    @Bean("credentialMatcher")
//    public CredentialMatcher credentialMatcher() {
//    	return new CredentialMatcher();
//    }


    /**
     * 加密器：这样一来数据库就可以是密文存储，为了演示我就不开启了
     *
     * @return HashedCredentialsMatcher
     */
//    @Bean
//    public HashedCredentialsMatcher hashedCredentialsMatcher() {
//        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
//        //散列算法:这里使用MD5算法;
//        hashedCredentialsMatcher.setHashAlgorithmName("md5");
//        //散列的次数，比如散列两次，相当于 md5(md5(""));
//        hashedCredentialsMatcher.setHashIterations(2);
//        return hashedCredentialsMatcher;
//    }


    /**
     * 扫描上下文，寻找所有的Advistor(通知器），将这些Advisor应用到所有符合切入点的Bean中
     *
     * @return
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        autoProxyCreator.setProxyTargetClass(true);
        return autoProxyCreator;
    }

    /**
     * 自定义realm
     *
     * @return
     */
//    @Bean(name = "authRealm")
//    public AuthRealm authRealm(@Qualifier("credentialMatcher") CredentialMatcher matcher) {
//        AuthRealm authRealm = new AuthRealm();
//        // 可以指定缓存
////        authRealm.setCacheManager(cacheManager);
////        authRealm.setCredentialsMatcher(matcher);
//        return authRealm;
//    }

    @Bean
    public DefaultWebSessionManager sessionManager(){
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setSessionValidationSchedulerEnabled(false);
        sessionManager.setSessionIdUrlRewritingEnabled(false);
        return sessionManager;
    }

    /**
     * securityManager
     *
     *
     * @param authRealm
     * @return
     */
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager securityManager(@Qualifier("authRealm") AuthRealm authRealm, SessionManager sessionManager) {
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        defaultWebSecurityManager.setRealm(authRealm);
        defaultWebSecurityManager.setSessionManager(sessionManager);
        defaultWebSecurityManager.setRememberMeManager(null);
        // <!-- 用户授权/认证信息Cache, 采用EhCache 缓存 -->
//        defaultWebSecurityManager.setCacheManager(getEhCacheManager());
        return defaultWebSecurityManager;
    }

    /**
     * 告诉spring 使用这个 securityManager
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(
            DefaultWebSecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }

    /**
     * shiro拦截器
     * 这里定义拦截规则
     *
     * @param securityManager 安全管理器
     * @return ShiroFilterFactoryBean
     */
    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // 必须设置 SecurityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);


        //oauth过滤
        Map<String, Filter> filters = new HashMap<>();
        filters.put("oauth2", new Oauth2Filter());

        shiroFilterFactoryBean.setFilters(filters);

        // 如果不设置默认会自动寻找Web工程根目录下的"/login"页面
//        shiroFilterFactoryBean.setLoginUrl("/login");
        // 登录成功后要跳转的连接
//        shiroFilterFactoryBean.setSuccessUrl("/index");
        // 设置未授权跳转的链接
//        shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");
        loadShiroFilterChain(shiroFilterFactoryBean);
        return shiroFilterFactoryBean;
    }

    /**
     * 将
     * @param securityFilter
     * @return
     */
    @Bean
    public FilterRegistrationBean securityFilterChain(AbstractShiroFilter securityFilter) {
        FilterRegistrationBean registration = new FilterRegistrationBean(securityFilter);
        registration.setOrder(Integer.MAX_VALUE-2);// 排在ZHandleDotDOFilter的前面
        registration.setName("shiroFilter");
        registration.setDispatcherTypes(DispatcherType.REQUEST);
        return registration;
    }

    /**
     * 加载shiroFilter权限控制规则（从数据库读取然后配置）
     */
    private void loadShiroFilterChain(ShiroFilterFactoryBean shiroFilterFactoryBean) {
        /////////////////////// 下面这些规则配置最好配置到配置文件中 ///////////////////////
        // TODO 重中之重啊，过滤顺序一定要根据自己需要排序
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        // 需要验证的写 authc 不需要的写 anon
        filterChainDefinitionMap.put("/resource/**", "anon");

        // 表示只有当前登录对象的角色为admin的时候才可以访问
        filterChainDefinitionMap.put("/admin", "roles[admin]");
        filterChainDefinitionMap.put("/doctor/**", "roles[doctor]");

        // 表示只有拿到了 edit的permission权限才可以访问这个接口
        filterChainDefinitionMap.put("/edit", "perms[edit]");
        filterChainDefinitionMap.put("/now/edit", "perms[editnow]");

        // 不需要拦截的访问地址， 在这里定义， 可以写到数据库中
        filterChainDefinitionMap.put("/tb/gateway/**", "anon");
        filterChainDefinitionMap.put("/login", "anon");
        filterChainDefinitionMap.put("/register", "anon");
        filterChainDefinitionMap.put("/loginUser", "anon");
//        filterChainDefinitionMap.put("/system/user/**", "anon");
        filterChainDefinitionMap.put("/captcha", "anon");
//        filterChainDefinitionMap.put("/system/mysqlImport/**", "anon");
        filterChainDefinitionMap.put("/testCaptcha", "anon");
        filterChainDefinitionMap.put("/getUserCaptcha", "anon");
        filterChainDefinitionMap.put("/verifyCode", "anon");
        filterChainDefinitionMap.put("/updatePasswd", "anon");
        filterChainDefinitionMap.put("/websocket/*", "anon");

        // 对外开放的Api
        filterChainDefinitionMap.put("/interServe/**", "anon");

        // anon：它对应的过滤器里面是空的,什么都没做
        log.info("##################从数据库读取权限规则，加载到shiroFilter中##################");

        // 不用注解也可以通过 API 方式加载权限规则
        Map<String, String> permissions = new LinkedHashMap<String, String>();
        permissions.put("/users/find", "perms[user:find]");
        filterChainDefinitionMap.putAll(permissions);
        filterChainDefinitionMap.put("/**", "user");
        filterChainDefinitionMap.put("/**", "oauth2");
        // 如果是开发者模式那么所有权限失效
        if(Constant.dev_mode) {
            filterChainDefinitionMap.put("/**", "anon");
        }

        // 如果用户的角色为超级管理员那么可以访问任何资源
//      filterChainDefinitionMap.put("/admin", "roles[admin]");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

    }
}
